/*
 * Edirol UA-1A driver
 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
 *
 * This driver is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2.
 *
 * This driver is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this driver.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * This driver is somewhat pointless because the standard snd-usb-audio driver
 * handles the UA-1A just fine; I'm using it only for testing or debugging.
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>

MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("Edirol UA-1A driver");
MODULE_LICENSE("GPL v2");
MODULE_SUPPORTED_DEVICE("{{Edirol,UA-1A}}");

static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "card index");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");

/* how many URBs are queued */
#define QUEUE_LENGTH	8

#define PLAYBACK_ENDPOINT	1
#define PLAYBACK_BUFFER_SIZE	(45 * 4)
#define CAPTURE_ENDPOINT	2
#define CAPTURE_BUFFER_SIZE	(48 * 4)

/* these INTF_* symbols are both USB interface numbers and array indices */
enum {
	INTF_CONTROL,
	INTF_PLAYBACK,
	INTF_CAPTURE,

	INTF_COUNT
};

struct ua1a {
	struct usb_device *dev;
	struct snd_card *card;
	struct usb_interface *intf[INTF_COUNT];
	struct snd_pcm *pcm;
	unsigned int slot_index;
	bool disconnect;
	struct ua1a_stream {
		spinlock_t lock;
		struct snd_pcm_substream *substream;
		unsigned int buffer_pos;
		unsigned int period_pos;
		bool running;
		u8 altsetting;
		struct urb *urbs[QUEUE_LENGTH];
	} playback, capture;
	unsigned int playback_phase;
};

static DEFINE_MUTEX(devices_mutex);
static DEFINE_MUTEX(free_urbs_mutex);
static unsigned int used_slots;
static struct usb_driver ua1a_driver;


static void ua1a_capture_complete(struct urb *urb)
{
	struct ua1a *ua = urb->context;
	struct ua1a_stream *stream = &ua->capture;
	struct snd_pcm_runtime *runtime;
	unsigned long flags;
	unsigned int frames, frames1;
	u8 *dest;
	int err;
	bool period_elapsed = 0;

	if (urb->status == -ENOENT ||		/* unlinked */
	    urb->status == -ENODEV ||		/* device removed */
	    urb->status == -ECONNRESET ||	/* unlinked */
	    urb->status == -ESHUTDOWN)		/* device disabled */
		goto xrun;

	spin_lock_irqsave(&stream->lock, flags);

	if (urb->status >= 0 &&
	    urb->iso_frame_desc[0].status >= 0 &&
	    stream->running) {
		/* copy data from the URB buffer into the ALSA ring buffer */
		runtime = stream->substream->runtime;
		frames = urb->iso_frame_desc[0].actual_length / 4;
		dest = runtime->dma_area + stream->buffer_pos * 4;
		if (stream->buffer_pos + frames <= runtime->buffer_size) {
			memcpy(dest, urb->transfer_buffer, frames * 4);
		} else {
			/* wrap around at end of ring buffer */
			frames1 = runtime->buffer_size - stream->buffer_pos;
			memcpy(dest, urb->transfer_buffer, frames1 * 4);
			memcpy(runtime->dma_area,
			       urb->transfer_buffer + frames1 * 4,
			       (frames - frames1) * 4);
		}

		stream->buffer_pos += frames;
		if (stream->buffer_pos >= runtime->buffer_size)
			stream->buffer_pos -= runtime->buffer_size;
		stream->period_pos += frames;
		if (stream->period_pos >= runtime->period_size) {
			stream->period_pos -= runtime->period_size;
			period_elapsed = 1;
		}
	}

	spin_unlock_irqrestore(&stream->lock, flags);

	urb->dev = ua->dev;
	err = usb_submit_urb(urb, GFP_ATOMIC);
	if (err < 0)
		goto xrun;

	if (period_elapsed)
		snd_pcm_period_elapsed(stream->substream);
	return;

xrun:
	snd_pcm_stop(stream->substream, SNDRV_PCM_STATE_XRUN);
}

static struct snd_pcm_hardware ua1a_pcm_hardware = {
	.info = SNDRV_PCM_INFO_MMAP |
		SNDRV_PCM_INFO_MMAP_VALID |
		SNDRV_PCM_INFO_BATCH |
		SNDRV_PCM_INFO_INTERLEAVED |
		SNDRV_PCM_INFO_BLOCK_TRANSFER,
	.formats = SNDRV_PCM_FMTBIT_S16_LE,
	.rates = SNDRV_PCM_RATE_44100,
	.rate_min = 44100,
	.rate_max = 44100,
	.channels_min = 2,
	.channels_max = 2,
	.buffer_bytes_max = 1024 * 1024,
	.period_bytes_min = 66 * 4,
	.period_bytes_max = 512 * 1024,
	.periods_min = 2,
	.periods_max = 2048,
};

static void free_urbs(struct ua1a *ua, struct ua1a_stream *stream)
{
	unsigned int i;

	mutex_lock(&free_urbs_mutex);
	for (i = 0; i < QUEUE_LENGTH; ++i)
		if (stream->urbs[i]) {
			usb_free_coherent(ua->dev,
				stream->urbs[i]->transfer_buffer_length,
				stream->urbs[i]->transfer_buffer,
				stream->urbs[i]->transfer_dma);
			usb_free_urb(stream->urbs[i]);
			stream->urbs[i] = NULL;
		}
	mutex_unlock(&free_urbs_mutex);
}

static int ua1a_open(struct snd_pcm_substream *substream,
		     struct ua1a_stream *stream, unsigned int pipe,
		     unsigned int buffer_size, usb_complete_t complete)
{
	struct ua1a *ua = substream->private_data;
	struct urb *urb;
	unsigned int i;

	for (i = 0; i < QUEUE_LENGTH; ++i) {
		urb = usb_alloc_urb(1, GFP_KERNEL);
		if (!urb)
			goto out_of_memory;
		stream->urbs[i] = urb;
		urb->pipe = pipe;
		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
		urb->transfer_buffer = usb_alloc_coherent(ua->dev, buffer_size,
							  GFP_KERNEL,
							  &urb->transfer_dma);
		if (!urb->transfer_buffer)
			goto out_of_memory;
		urb->transfer_buffer_length = buffer_size;
		urb->number_of_packets = 1;
		urb->interval = 1;
		urb->context = ua;
		urb->complete = complete;
		urb->iso_frame_desc[0].offset = 0;
		urb->iso_frame_desc[0].length = buffer_size;
	}
	stream->substream = substream;
	return 0;

out_of_memory:
	free_urbs(ua, stream);
	return -ENOMEM;
}

static int ua1a_capture_open(struct snd_pcm_substream *substream)
{
	struct ua1a *ua = substream->private_data;

	substream->runtime->hw = ua1a_pcm_hardware;
	return ua1a_open(substream, &ua->capture,
			 usb_rcvisocpipe(ua->dev, CAPTURE_ENDPOINT),
			 CAPTURE_BUFFER_SIZE, ua1a_capture_complete);
}

static int ua1a_capture_close(struct snd_pcm_substream *substream)
{
	struct ua1a *ua = substream->private_data;

	ua->capture.substream = NULL;
	free_urbs(ua, &ua->capture);
	return 0;
}

static void ua1a_kill_urbs(struct ua1a_stream *stream)
{
	unsigned int i;

	for (i = 0; i < QUEUE_LENGTH; ++i)
		usb_kill_urb(stream->urbs[i]);
}

static void ua1a_unlink_urbs(struct ua1a_stream *stream)
{
	unsigned int i;

	for (i = 0; i < QUEUE_LENGTH; ++i)
		usb_unlink_urb(stream->urbs[i]);
}

static int ua1a_hw_params(struct snd_pcm_substream *substream,
			  struct ua1a_stream *stream, unsigned int intf_number,
			  struct snd_pcm_hw_params *hw_params)
{
	struct ua1a *ua = substream->private_data;
	int err;

	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
					       params_buffer_bytes(hw_params));
	if (err < 0)
		return err;

	if (stream->altsetting != 1) {
		ua1a_kill_urbs(stream);

		err = usb_set_interface(ua->dev, intf_number, 1);
		if (err < 0) {
			dev_err(&ua->dev->dev,
				"cannot initialize interface; error %d\n", err);
			return err;
		}
		stream->altsetting = 1;
	}
	return 0;
}

static int ua1a_capture_hw_params(struct snd_pcm_substream *substream,
				  struct snd_pcm_hw_params *hw_params)
{
	struct ua1a *ua = substream->private_data;

	return ua1a_hw_params(substream, &ua->capture,
			      INTF_CAPTURE, hw_params);
}

static void ua1a_hw_free(struct snd_pcm_substream *substream,
			 struct ua1a_stream *stream, unsigned int intf_number)
{
	struct ua1a *ua = substream->private_data;
	int err;

	ua1a_kill_urbs(stream);

	if (!ua->disconnect && stream->altsetting != 0) {
		err = usb_set_interface(ua->dev, intf_number, 0);
		if (err < 0)
			dev_warn(&ua->dev->dev,
				 "interface reset failed; error %d\n", err);
		stream->altsetting = 0;
	}

	snd_pcm_lib_free_vmalloc_buffer(substream);
}

static int ua1a_capture_hw_free(struct snd_pcm_substream *substream)
{
	struct ua1a *ua = substream->private_data;

	ua1a_hw_free(substream, &ua->capture, INTF_CAPTURE);
	return 0;
}

static int ua1a_submit_urbs(struct ua1a *ua, struct ua1a_stream *stream)
{
	unsigned int i;
	int err;

	for (i = 0; i < QUEUE_LENGTH; ++i) {
		stream->urbs[i]->dev = ua->dev;
		err = usb_submit_urb(stream->urbs[i], GFP_KERNEL);
		if (err < 0) {
			dev_err(&ua->dev->dev,
				"cannot submit URBs; error %d\n", err);
			ua1a_kill_urbs(stream);
			return err;
		}
	}
	return 0;
}

static int ua1a_capture_prepare(struct snd_pcm_substream *substream)
{
	struct ua1a *ua = substream->private_data;

	ua1a_kill_urbs(&ua->capture);

	ua->capture.buffer_pos = 0;
	ua->capture.period_pos = 0;

	return ua1a_submit_urbs(ua, &ua->capture);
}

static void ua1a_start(struct ua1a_stream *stream)
{
	spin_lock(&stream->lock);
	stream->running = 1;
	spin_unlock(&stream->lock);
}

static void ua1a_stop(struct ua1a_stream *stream)
{
	spin_lock(&stream->lock);
	stream->running = 0;
	spin_unlock(&stream->lock);
	ua1a_unlink_urbs(stream);
}

static int ua1a_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
	struct ua1a *ua = substream->private_data;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		ua1a_start(&ua->capture);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
		ua1a_stop(&ua->capture);
		break;
	default:
		return -EINVAL;
	}
	return 0;
}

static inline snd_pcm_uframes_t ua1a_pointer(struct ua1a_stream *stream)
{
	unsigned long flags;
	unsigned int pos;

	spin_lock_irqsave(&stream->lock, flags);
	pos = stream->buffer_pos;
	spin_unlock_irqrestore(&stream->lock, flags);
	return pos;
}

static snd_pcm_uframes_t
ua1a_capture_pointer(struct snd_pcm_substream *substream)
{
	struct ua1a *ua = substream->private_data;

	return ua1a_pointer(&ua->capture);
}

static struct snd_pcm_ops ua1a_capture_ops = {
	.open = ua1a_capture_open,
	.close = ua1a_capture_close,
	.ioctl = snd_pcm_lib_ioctl,
	.hw_params = ua1a_capture_hw_params,
	.hw_free = ua1a_capture_hw_free,
	.prepare = ua1a_capture_prepare,
	.trigger = ua1a_capture_trigger,
	.pointer = ua1a_capture_pointer,
	.page = snd_pcm_lib_get_vmalloc_page,
	.mmap = snd_pcm_lib_mmap_vmalloc,
};

static void ua1a_card_free(struct snd_card *card)
{
#if 0
	struct ua1a *ua = card->private_data;
#endif
}

static int ua1a_probe(struct usb_interface *interface,
		      const struct usb_device_id *usb_id)
{
	struct snd_card *card;
	struct ua1a *ua;
	unsigned int intf_claimed = 0;
	unsigned int slot_index, i;
	int err;

	mutex_lock(&devices_mutex);

	for (slot_index = 0; slot_index < SNDRV_CARDS; ++slot_index)
		if (enable[slot_index] && !(used_slots & (1 << slot_index)))
			break;
	if (slot_index >= SNDRV_CARDS) {
		mutex_unlock(&devices_mutex);
		return -ENOENT;
	}
	err = snd_card_create(index[slot_index], id[slot_index], THIS_MODULE,
			      sizeof(*ua), &card);
	if (err < 0) {
		mutex_unlock(&devices_mutex);
		return err;
	}
	card->private_free = ua1a_card_free;
	ua = card->private_data;
	ua->dev = interface_to_usbdev(interface);
	ua->card = card;
	ua->slot_index = slot_index;
	spin_lock_init(&ua->playback.lock);
	spin_lock_init(&ua->capture.lock);

	strcpy(card->driver, "UA-1A");
	strcpy(card->shortname, "UA-1A");
	strcpy(card->longname, "Edirol UA-1A at ");
	i = strlen(card->longname);
	usb_make_path(ua->dev, card->longname + i, sizeof(card->longname) - i);

	for (i = 0; i < INTF_COUNT; ++i) {
		ua->intf[i] = usb_ifnum_to_if(ua->dev, i);
		if (!ua->intf[i]) {
			err = -ENXIO;
			goto probe_error;
		}
		if (ua->intf[i] != interface) {
			err = usb_driver_claim_interface(&ua1a_driver, ua->intf[i], ua);
			if (err < 0) {
				err = -EBUSY;
				goto probe_error;
			}
			intf_claimed |= 1 << i;
		}
	}

	snd_card_set_dev(card, &interface->dev);

	err = snd_pcm_new(card, "UA-1A", 0, 1, 1, &ua->pcm);
	if (err < 0)
		goto probe_error;
	ua->pcm->private_data = ua;
	strcpy(ua->pcm->name, "UA-1A");
	snd_pcm_set_ops(ua->pcm,
			SNDRV_PCM_STREAM_CAPTURE, &ua1a_capture_ops);

	err = snd_card_register(card);
	if (err < 0)
		goto probe_error;
	usb_set_intfdata(interface, ua);
	used_slots |= 1 << slot_index;

	mutex_unlock(&devices_mutex);
	return 0;

probe_error:
	for (i = 0; i < INTF_COUNT; ++i)
		if (intf_claimed & (1 << i)) {
			usb_set_intfdata(ua->intf[i], NULL);
			usb_driver_release_interface(&ua1a_driver, ua->intf[i]);
		}
	mutex_unlock(&devices_mutex);
	snd_card_free(card);
	return err;
}

static void ua1a_disconnect(struct usb_interface *interface)
{
	struct ua1a *ua = usb_get_intfdata(interface);
	unsigned int i;

	if (!ua)
		return;

	mutex_lock(&devices_mutex);

	ua->disconnect = 1;

	/* make sure that userspace cannot create new requests */
	snd_card_disconnect(ua->card);

	/* make sure that there are no pending USB requests */
	mutex_lock(&free_urbs_mutex);
	if (ua->capture.urbs[0])
		ua1a_kill_urbs(&ua->capture);
	mutex_unlock(&free_urbs_mutex);

	for (i = 0; i < INTF_COUNT; ++i)
		if (ua->intf[i] != interface) {
			usb_set_intfdata(ua->intf[i], NULL);
			usb_driver_release_interface(&ua1a_driver, ua->intf[i]);
		}
	usb_set_intfdata(interface, NULL);

	used_slots &= ~(1 << ua->slot_index);

	snd_card_free_when_closed(ua->card);

	mutex_unlock(&devices_mutex);
}

static struct usb_device_id ua1a_ids[] = {
	{ USB_DEVICE(0x0582, 0x0018) },
	{ }
};
MODULE_DEVICE_TABLE(usb, ua1a_ids);

static struct usb_driver ua1a_driver = {
	.name = "UA-1A",
	.id_table = ua1a_ids,
	.probe = ua1a_probe,
	.disconnect = ua1a_disconnect,
#if 0
	.suspend = ua1a_suspend,
	.resume = ua1a_resume,
#endif
};

static int __init alsa_card_ua1a_init(void)
{
	return usb_register(&ua1a_driver);
}

static void __exit alsa_card_ua1a_exit(void)
{
	usb_deregister(&ua1a_driver);
	mutex_destroy(&devices_mutex);
	mutex_destroy(&free_urbs_mutex);
}

module_init(alsa_card_ua1a_init);
module_exit(alsa_card_ua1a_exit);
